from fastapi import APIRouter, UploadFile, File, HTTPException, Form
import os
import uuid
from fastapi.responses import JSONResponse
import shutil
import sys
import importlib.util
import zipfile
from typing import List, Optional

file_folder_upload_router = APIRouter(
    prefix="/upload_file_folder_url",
    tags=["file_folder_upload"],
    responses={404: {"description": "Not found"}},
)
# 配置上传文件夹
UPLOAD_FOLDER = 'uploads'
if not os.path.exists(UPLOAD_FOLDER):
    os.makedirs(UPLOAD_FOLDER)

# 设置最大文件大小限制（10MB）
MAX_FILE_SIZE = 10 * 1024 * 1024  # 10MB in bytes

# 辅助函数: 检查文件大小
def check_file_size(file: UploadFile, max_size: int = MAX_FILE_SIZE):
    """
    检查文件大小是否超出限制
    
    Args:
        file: 上传的文件
        max_size: 最大允许大小(字节)
        
    Returns:
        文件大小(字节)
        
    Raises:
        HTTPException: 如果文件大小超出限制
    """
    file.file.seek(0, 2)  # 移动到文件末尾
    file_size = file.file.tell()  # 获取文件大小
    file.file.seek(0)  # 重置文件指针到开头
    
    if file_size > max_size:
        raise HTTPException(status_code=400, detail=f"文件大小超过限制（最大{max_size/1024/1024:.1f}MB）")
    
    return file_size

# 辅助函数: 检查文件类型
def check_image_type(file: UploadFile):
    """
    检查文件是否为图片类型
    
    Args:
        file: 上传的文件
        
    Raises:
        HTTPException: 如果文件不是图片类型
    """
    content_type = file.content_type
    if not content_type or not content_type.startswith('image/'):
        raise HTTPException(status_code=400, detail="只接受图片文件")

# 辅助函数: 获取服务器URL
def get_server_url():
    """
    获取main.py中定义的SERVER_URL
    
    Returns:
        服务器URL字符串
    """
    try:
        import main
        return main.SERVER_URL
    except (ImportError, AttributeError):
        # 如果无法导入，使用默认值
        return "http://localhost:8007"

@file_folder_upload_router.post("/image_upload")
async def upload_image(file: UploadFile = File(...)):
    """
    上传图片并返回可访问的URL
    """
    try:
        # 检查文件是否为图片
        check_image_type(file)
        
        # 检查文件大小
        check_file_size(file)
        
        # 获取文件扩展名
        file_extension = os.path.splitext(file.filename)[1] if file.filename else ""
        
        # 生成唯一文件名
        unique_filename = f"{uuid.uuid4().hex}{file_extension}"
        
        # 文件路径
        file_path = os.path.join(UPLOAD_FOLDER, unique_filename)
        
        # 保存上传的文件
        with open(file_path, "wb") as buffer:
            shutil.copyfileobj(file.file, buffer)
        
        # 获取服务器URL
        server_url = get_server_url()
        
        # 构建完整URL
        file_url = f"{server_url}/uploads/{unique_filename}"
        
        return JSONResponse({"url": file_url, "filename": unique_filename})
    
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"上传失败: {str(e)}")
    finally:
        # 关闭上传的文件
        file.file.close()

@file_folder_upload_router.post("/file_upload")
async def upload_file(file: UploadFile = File(...)):
    """
    上传任意类型的文件并返回可访问的URL
    """
    try:
        # 检查文件大小
        check_file_size(file)
        
        # 获取文件扩展名
        file_extension = os.path.splitext(file.filename)[1] if file.filename else ""
        
        # 生成唯一文件名
        unique_filename = f"{uuid.uuid4().hex}{file_extension}"
        
        # 文件路径
        file_path = os.path.join(UPLOAD_FOLDER, unique_filename)
        
        # 保存上传的文件
        with open(file_path, "wb") as buffer:
            shutil.copyfileobj(file.file, buffer)
        
        # 获取服务器URL
        server_url = get_server_url()
        
        # 构建完整URL
        file_url = f"{server_url}/uploads/{unique_filename}"
        
        return JSONResponse({"url": file_url, "filename": unique_filename})
    
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"上传失败: {str(e)}")
    finally:
        # 关闭上传的文件
        file.file.close()

# 处理上传文件并压缩为zip的函数
async def _upload_files_to_zip(
    files: List[UploadFile],
    folder_name: Optional[str] = None
):
    """
    上传多个文件，将它们保存到一个文件夹并压缩为zip文件，返回zip文件的URL
    
    - **files**: 要上传的文件列表
    - **folder_name**: 自定义文件夹名称（可选）
    
    返回:
    - **zip_url**: zip文件的URL
    - **zip_filename**: zip文件名
    - **folder_id**: 文件夹唯一标识
    - **files_count**: 上传的文件数量
    - **files_info**: 每个文件的详细信息列表
    """
    try:
        # 验证是否提供了文件
        if not files or len(files) == 0:
            raise HTTPException(status_code=400, detail="没有提供文件")
        
        # 保存原始文件列表，以便正确关闭
        uploaded_files = list(files)
        
        # 生成唯一文件夹ID
        folder_id = uuid.uuid4().hex
        
        # 确定文件夹名称
        if folder_name:
            # 对文件夹名称进行安全处理，避免路径注入
            safe_folder_name = "".join(c for c in folder_name if c.isalnum() or c in "-_ ")
            folder_name_final = f"{safe_folder_name}_{folder_id[:8]}"
        else:
            folder_name_final = f"folder_{folder_id}"
        
        # 创建文件夹路径
        folder_path = os.path.join(UPLOAD_FOLDER, folder_name_final)
        os.makedirs(folder_path, exist_ok=True)
        
        # 处理每个文件
        files_info = []
        total_size = 0
        
        for file in uploaded_files:
            # 检查文件大小
            file_size = check_file_size(file)
            total_size += file_size
            
            # 获取安全的文件名
            original_filename = file.filename
            file_extension = os.path.splitext(original_filename)[1] if original_filename else ""
            safe_filename = f"{uuid.uuid4().hex[:8]}_{original_filename}"
            
            # 文件存储路径
            file_path = os.path.join(folder_path, safe_filename)
            
            # 保存文件
            with open(file_path, "wb") as buffer:
                shutil.copyfileobj(file.file, buffer)
            
            # 记录文件信息
            files_info.append({
                "original_name": original_filename,
                "saved_name": safe_filename,
                "size": file_size,
                "content_type": file.content_type,
                "path": os.path.abspath(file_path)
            })
        
        # 创建ZIP文件
        zip_filename = f"{folder_name_final}.zip"
        zip_path = os.path.join(UPLOAD_FOLDER, zip_filename)
        
        # 压缩文件夹内容
        with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zipf:
            for root, dirs, folder_files in os.walk(folder_path):
                for file_name in folder_files:
                    file_path = os.path.join(root, file_name)
                    # 获取相对路径，这样zip内的路径结构才正确
                    arc_name = os.path.relpath(file_path, folder_path)
                    zipf.write(file_path, arcname=arc_name)
        
        # 获取服务器URL
        server_url = get_server_url()
        
        # 构建zip文件URL
        zip_url = f"{server_url}/uploads/{zip_filename}"
        
        # 返回结果
        return JSONResponse({
            "success": True,
            "zip_url": zip_url,
            "zip_filename": zip_filename,
            "zip_path": os.path.abspath(zip_path),
            "folder_id": folder_id,
            "folder_name": folder_name_final,
            "folder_path": os.path.abspath(folder_path),
            "files_count": len(files_info),
            "total_size_bytes": total_size,
            "total_size_mb": round(total_size / (1024 * 1024), 2),
            "files_info": files_info
        })
    
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"上传文件并创建zip压缩包失败: {str(e)}")
    finally:
        # 关闭所有文件
        for file in uploaded_files:
            file.file.close()

# 处理上传文件并保持结构压缩为zip的函数
async def _upload_files_with_structure_to_zip(
    files: List[UploadFile],
    folder_name: Optional[str] = None,
    file_paths: List[str] = None
):
    """
    上传多个文件，保持文件结构，将它们保存到一个文件夹并压缩为zip文件，返回zip文件的URL
    
    - **files**: 要上传的文件列表
    - **folder_name**: 自定义文件夹名称（可选）
    - **file_paths**: 每个文件在文件夹中的相对路径列表，顺序必须与files一致
    
    返回:
    - **zip_url**: zip文件的URL
    - **zip_filename**: zip文件名
    - **folder_id**: 文件夹唯一标识
    - **files_count**: 上传的文件数量
    - **files_info**: 每个文件的详细信息列表
    """
    try:
        # 验证是否提供了文件
        if not files or len(files) == 0:
            raise HTTPException(status_code=400, detail="没有提供文件")
        
        # 保存原始文件列表，以便正确关闭
        uploaded_files = list(files)
        
        # 验证文件和路径数量是否匹配
        if len(uploaded_files) != len(file_paths):
            raise HTTPException(status_code=400, detail="文件数量与路径数量不匹配")
        
        # 生成唯一文件夹ID
        folder_id = uuid.uuid4().hex
        
        # 确定文件夹名称
        if folder_name:
            # 对文件夹名称进行安全处理，避免路径注入
            safe_folder_name = "".join(c for c in folder_name if c.isalnum() or c in "-_ ")
            folder_name_final = f"{safe_folder_name}_{folder_id[:8]}"
        else:
            folder_name_final = f"folder_{folder_id}"
        
        # 创建文件夹路径
        folder_path = os.path.join(UPLOAD_FOLDER, folder_name_final)
        os.makedirs(folder_path, exist_ok=True)
        
        # 处理每个文件
        files_info = []
        total_size = 0
        
        for i, (file, relative_path) in enumerate(zip(uploaded_files, file_paths)):
            # 检查文件大小
            file_size = check_file_size(file)
            total_size += file_size
            
            # 安全处理相对路径
            # 移除可能导致路径注入的组件
            safe_relative_path = os.path.normpath(relative_path).lstrip(os.sep).lstrip('.')
            
            # 目标文件路径
            target_dir = os.path.join(folder_path, os.path.dirname(safe_relative_path))
            os.makedirs(target_dir, exist_ok=True)
            
            # 获取原始文件名
            original_filename = os.path.basename(safe_relative_path) or file.filename
            
            # 保存文件
            file_path = os.path.join(target_dir, original_filename)
            with open(file_path, "wb") as buffer:
                shutil.copyfileobj(file.file, buffer)
            
            # 记录文件信息
            files_info.append({
                "original_name": file.filename,
                "saved_path": os.path.relpath(file_path, folder_path),
                "absolute_path": os.path.abspath(file_path),
                "size": file_size,
                "content_type": file.content_type
            })
        
        # 创建ZIP文件
        zip_filename = f"{folder_name_final}.zip"
        zip_path = os.path.join(UPLOAD_FOLDER, zip_filename)
        
        # 压缩文件夹内容
        with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zipf:
            for root, dirs, folder_files in os.walk(folder_path):
                for file_name in folder_files:
                    file_path = os.path.join(root, file_name)
                    # 获取相对路径，这样zip内的路径结构才正确
                    arc_name = os.path.relpath(file_path, folder_path)
                    zipf.write(file_path, arcname=arc_name)
        
        # 获取服务器URL
        server_url = get_server_url()
        
        # 构建zip文件URL
        zip_url = f"{server_url}/uploads/{zip_filename}"
        
        # 返回结果
        return JSONResponse({
            "success": True,
            "zip_url": zip_url,
            "zip_filename": zip_filename,
            "zip_path": os.path.abspath(zip_path),
            "folder_id": folder_id,
            "folder_name": folder_name_final,
            "folder_path": os.path.abspath(folder_path),
            "files_count": len(files_info),
            "total_size_bytes": total_size,
            "total_size_mb": round(total_size / (1024 * 1024), 2),
            "files_info": files_info
        })
    
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"上传文件并创建zip压缩包失败: {str(e)}")
    finally:
        # 关闭所有文件
        for file in uploaded_files:
            file.file.close()

@file_folder_upload_router.post("/files_upload2zip")
async def files_upload2zip(
    files: List[UploadFile] = File(...),
    folder_name: Optional[str] = Form(None, description="文件夹名称，可选，默认生成随机名称")
):
    """
    上传多个文件，将它们保存到一个文件夹并压缩为zip文件，返回zip文件的URL
    
    - **files**: 要上传的文件列表
    - **folder_name**: 自定义文件夹名称（可选）
    
    返回:
    - **zip_url**: zip文件的URL
    - **zip_filename**: zip文件名
    - **folder_id**: 文件夹唯一标识
    - **files_count**: 上传的文件数量
    - **files_info**: 每个文件的详细信息列表
    """
    return await _upload_files_to_zip(files, folder_name)

@file_folder_upload_router.post("/files_with_structure2zip")
async def files_with_structure2zip(
    files: List[UploadFile] = File(...),
    folder_name: Optional[str] = Form(None, description="文件夹名称，可选，默认生成随机名称"),
    file_paths: List[str] = Form(..., description="每个文件对应的相对路径")
):
    """
    上传多个文件，保持文件结构，将它们保存到一个文件夹并压缩为zip文件，返回zip文件的URL
    
    - **files**: 要上传的文件列表
    - **folder_name**: 自定义文件夹名称（可选）
    - **file_paths**: 每个文件在文件夹中的相对路径列表，顺序必须与files一致
    
    返回:
    - **zip_url**: zip文件的URL
    - **zip_filename**: zip文件名
    - **folder_id**: 文件夹唯一标识
    - **files_count**: 上传的文件数量
    - **files_info**: 每个文件的详细信息列表
    """
    return await _upload_files_with_structure_to_zip(files, folder_name, file_paths)

# 为folder_upload_router添加路由
@file_folder_upload_router.post("/files_upload2zip")
async def folder_files_upload2zip(
    files: List[UploadFile] = File(...),
    folder_name: Optional[str] = Form(None, description="文件夹名称，可选，默认生成随机名称")
):
    """
    上传多个文件，将它们保存到一个文件夹并压缩为zip文件，返回zip文件的URL
    
    - **files**: 要上传的文件列表
    - **folder_name**: 自定义文件夹名称（可选）
    
    返回:
    - **zip_url**: zip文件的URL
    - **zip_filename**: zip文件名
    - **folder_id**: 文件夹唯一标识
    - **files_count**: 上传的文件数量
    - **files_info**: 每个文件的详细信息列表
    """
    return await _upload_files_to_zip(files, folder_name)

@file_folder_upload_router.post("/files_with_structure2zip")
async def folder_files_with_structure2zip(
    files: List[UploadFile] = File(...),
    folder_name: Optional[str] = Form(None, description="文件夹名称，可选，默认生成随机名称"),
    file_paths: List[str] = Form(..., description="每个文件对应的相对路径")
):
    """
    上传多个文件，保持文件结构，将它们保存到一个文件夹并压缩为zip文件，返回zip文件的URL
    
    - **files**: 要上传的文件列表
    - **folder_name**: 自定义文件夹名称（可选）
    - **file_paths**: 每个文件在文件夹中的相对路径列表，顺序必须与files一致
    
    返回:
    - **zip_url**: zip文件的URL
    - **zip_filename**: zip文件名
    - **folder_id**: 文件夹唯一标识
    - **files_count**: 上传的文件数量
    - **files_info**: 每个文件的详细信息列表
    """
    return await _upload_files_with_structure_to_zip(files, folder_name, file_paths) 